const vnode = (sel, data, children, text, elm) => {
  let key = data.key;
  return {
    sel,
    data,
    children,
    text,
    elm,
    key,
  };
}; //vnode函数

const h = (sel, data, params) => {
  if (typeof params === "string") {
    return vnode(sel, data, undefined, params, undefined);
  } else if (Array.isArray(params)) {
    let children = [];
    params.forEach(item => {
      children.push(item);
    });
    return vnode(sel, data, children, undefined, undefined);
  }
}; //h函数

const createElement = Vnode => {
  let domNode = document.createElement(Vnode.sel);
  if (Vnode.children === undefined) {
    domNode.innerText = Vnode.text;
    //只有一个新节点
  } else if (Array.isArray(Vnode.children)) {
    //新节点有子元素递归创建
    Vnode.children.forEach(item => {
      let childDom = createElement(item);
      domNode.appendChild(childDom);
    });
  }
  Vnode.elm = domNode;
  return domNode;
}; //创建真实节点createElement函数
const sameVnode = (vNode1, vNode2) => {
  return vNode1.key == vNode2.key;
}; //判断是否是同一个节点函数
const updateChildren = (parentElm, oldCh, newCh) => {
  console.log(parentElm);
  let oldStartIdx = 0; //旧前指针
  let newStartIdx = 0; //新前指针
  let oldEndIdx = oldCh.length - 1; //旧后指针
  let newEndIdx = oldCh.length - 1; //新后指针

  let oldStartVnode = oldCh[0]; //旧前虚拟DOM
  let newStartVnode = oldCh[0]; //新前虚拟DOM
  let oldEndVnode = oldCh[oldEndIdx]; //旧后虚拟节点
  let newEndVnode = oldCh[oldEndIdx]; //新后虚拟DOM
  while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
    if (oldStartVnode === undefined) {
      //旧前是undefined，旧前指针往下
      oldStartVnode = oldCh[oldStartIdx++];
    }
    if (oldEndVnode === undefined) {
      //旧后是undefined，旧后指针往上
      oldEndVnode = oldCh[oldEndVnode--];
    } else if (sameVnode(oldStartVnode, newStartVnode)) {
      //第一种情况：旧前 和 新前
      patchVnode(oldStartVnode, newStartVnode);
      // 执行oldVnode,newVnode是同一个节点函数
      if (newStartVnode) newStartVnode.elm = oldStartVnode?.elm; //修正elm
      oldStartVnode = oldCh[oldStartIdx++];
      newStartVnode = newCh[newStartIdx++];
      //修改新旧指针往下走
    } else if (sameVnode(oldStartVnode, newStartVnode)) {
      //第二种情况：旧后 和 新后
      patchVnode(oldEndVnode, newEndVnode);
      // 执行oldVnode,newVnode是同一个节点函数
      if (newEndVnode) newEndVnode.elm = oldEndVnode?.elm; //修正elm
      oldEndVnode = oldCh[oldEndVnode--];
      newEndVnode = newCh[newEndVnode--];
      //修改新旧指针往上走
    } else if (sameVnode(oldStartVnode, newStartVnode)) {
      //第三种情况：旧前 和 新后
      patchVnode(oldStartVnode, newEndVnode);
      // 执行oldVnode,newVnode是同一个节点函数
      if (newEndVnode) newEndVnode.elm = oldStartVnode?.elm; //修正elm
      parentElm.insertBefore(oldStartVnode.elm, oldEndVnode.elm.nextSibling);
      //把旧前指定的节点移动到旧后指向的节点的后面
      oldStartVnode = oldCh[oldStartIdx++];
      //旧前指针往下
      newEndVnode = newCh[newEndVnode--];
      //新后指针往上
    } else if (sameVnode(oldStartVnode, newStartVnode)) {
      //第四种情况：旧后 和 新前
      patchVnode(oldStartVnode, newEndVnode);
      // 执行oldVnode,newVnode是同一个节点函数
      if (newEndVnode) newStartVnode.elm = oldEndVnode?.elm; //修正elm
      parentElm.insertBefore(oldEndVnode.elm, oldStartVnode.elm);
      //将旧后指定的节点移动到旧前指向的节点的前面
      oldEndVnode = oldCh[--oldEndIdx];
      //旧后指针往上
      newStartVnode = newCh[++newStartIdx];
      //新前指针往下
    } else {
      //第五种情况：以上都不满足条件 ===》查找
      //创建一个对象，存虚拟节点的（判断新旧有没有相同节点）
      const keyMap = {};
      for (let i = oldStartIdx; i <= oldEndIdx; i++) {
        const key = oldCh[i]?.key;
        if (key) keyMap[key] = i;
      }
      let idxInOld = keyMap[newStartVnode.key];
      //在旧节点中寻找新前指向的节点
      if (idxInOld) {
        //如果有，说明数据在新旧虚拟节点中都存在
        const elmMove = oldCh[idxInOld];
        patchVnode(elmMove, newStartVnode);
        oldCh[idxInOld] = undefined;
        //处理过的节点，在旧虚拟节点的数组中，设置为undefined
        parentElm?.insertBefore(elmMove.elm, oldStartVnode.elm);
      } else {
        //如果没有找到==》说明是一个新的节点【创建】
        parentElm.insertBefore(createElement(newStartVnode), oldStartVnode.elm);
      }
      newStartVnode = newCh[++newStartIdx];
      //新数据（指针）+1
    }
  }
  if (oldStartIdx > oldEndIdx) {
    //结束while 只有俩种情况 （新增和删除）
    //1. oldStartIdx > oldEndIdx
    //2. newStartIdx > newEndIdx
    const before = newCh[newEndIdx + 1] ? newCh[newEndIdx + 1].elm : null;
    for (let i = newStartIdx; i <= newEndIdx; i++) {
      parentElm.insertBefore(createElement(newCh[i]), before);
    }
  } else {
    //进入删除操作

    for (let i = oldStartIdx; i <= oldEndIdx; i++) {
      parentElm.removeChild(oldCh[i].elm);
    }
  }
};

const patchVnode = (oldVnode, newVnode) => {
  //新的节点没有子节点
  console.log(oldVnode);
  if (newVnode.children === undefined) {
    if (newVnode.text !== oldVnode.text) {
      //文本不相同直接替换
      oldVnode.elm.innerText = newVnode.text;
    }
  } //新的有子节点
  else {
    //新的虚拟节点有  ，  旧的虚拟节点有
    if (oldVnode.children !== undefined && oldVnode.children.length > 0) {
      //  diff核心
      console.log("新旧都有children");
      console.log(oldVnode.elm);
      updateChildren(oldVnode.elm, oldVnode.children, newVnode.children);
    } else {
      //新的虚拟节点有  ，  旧的虚拟节点没有
      //把旧节点的内容 清空
      oldVnode.elm.innerHTML = "";
      //遍历新的 子节点 ， 创建dom元素，添加到页面中
      for (let child of newVnode.children) {
        let childDom = createElement(child);
        oldVnode.elm.appendChild(childDom);
      }
    }
  }
};
//oldVnode,newVnode是同一个节点
const patch = (oldVnode, newVnode) => {
  //判断是否是真实DOM，不是则包装成虚拟DOM
  if (oldVnode.sel == undefined) {
    oldVnode = vnode(
      oldVnode.tagName.toLowerCase(), //sel
      {}, //data
      [],
      undefined,
      oldVnode
    );
  }
  //判断oldVnode，newVnode是不是同一个节点，
  if (oldVnode.sel === newVnode.sel) {
    //是同一个节点
    patchVnode(oldVnode, newVnode);
  } else {
    //不是同一个节点 直接删除，创建新节点
    let newVnodeElm = createElement(newVnode);
    let oldVnodeElm = oldVnode.elm;
    if (newVnodeElm) {
      //创建新节点
      oldVnodeElm.parentNode.insertBefore(newVnodeElm, oldVnodeElm);
    }
    oldVnodeElm.parentNode.removeChild(oldVnodeElm);
    //删除旧节点
  }
};

let container = document.getElementById("container");

let vnode1 = h("ul", {}, [
  h("li", { key: "a" }, "a"),
  h("li", { key: "b" }, "b"),
  h("li", { key: "c" }, "c"),
  h("li", { key: "d" }, "d"),
  h("li", { key: "e" }, "e"),
]);
let vnode2 = h("ul", {}, [
  h("li", { key: "c" }, "c"),
  h("li", { key: "b" }, "b"),
  h("li", { key: "e" }, "e"),
  h("li", { key: "f" }, "f"),
  h("li", { key: "k" }, "k"),
]);

patch(container, vnode2);
patch(vnode1, vnode2);
